package day30.homework;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Java IO流序列化操作演示类
 * 该类演示了如何使用Java的序列化机制将对象持久化存储到文件中，
 * 以及如何从文件中读取并恢复这些对象
 * 
 * 序列化是Java提供的一种对象持久化机制，可以将内存中的对象状态保存到文件或数据库中，
 * 以便在需要时重新恢复对象的状态
 * 
 * 本类中涉及的核心知识点包括：
 * 1. ObjectOutputStream和ObjectInputStream的使用
 * 2. Serializable接口的作用和实现原理
 * 3. try-catch-finally异常处理机制
 * 4. 资源管理和流的正确关闭方式
 * 5. 文件I/O操作的基本流程
 */
public class hw1 {
    
    /**
     * save()方法：创建三个Person类型对象，将它们使用序列化手段写入本地文件中持久化存储
     * 
     * 序列化过程详解：
     * 1. 创建ObjectOutputStream对象输出流，用于将对象写入文件
     * 2. 实例化三个Person对象，分别代表不同的人的信息
     * 3. 使用writeObject()方法将对象序列化并写入文件
     * 4. 处理可能发生的IOException异常
     * 5. 在finally块中确保流资源被正确关闭
     * 
     * 序列化的意义：
     * - 将内存中的对象状态保存到持久化存储介质中（如文件、数据库）
     * - 实现对象在网络中的传输（如RMI远程方法调用）
     * - 实现对象的深拷贝功能
     * - 在分布式系统中传递对象状态
     */
    public static void save() {
        // 声明对象输出流引用，初始化为null，用于后续判断是否需要关闭资源
        // 使用ObjectOutputStream可以将实现了Serializable接口的对象序列化并写入到输出流中
        ObjectOutputStream oos = null;
        try {
            // 创建文件输出流FileOutputStream，指定输出文件名为"persons.dat"
            // FileOutputStream是字节输出流，用于将数据写入到文件中
            // "persons.dat"是相对路径，文件将被创建在当前工作目录下
            // 创建ObjectOutputStream对象，将文件输出流作为参数传入
            // ObjectOutputStream是对象输出流，可以将Java对象序列化为字节流
            oos = new ObjectOutputStream(new FileOutputStream("persons.dat"));
            
            // 创建三个Person对象实例，分别传入姓名、年龄和地址参数
            // Person类必须实现Serializable接口才能被序列化
            // 每个对象在内存中都有独立的地址空间和属性值
            Person person1 = new Person("张三", 25, "北京市朝阳区");
            Person person2 = new Person("李四", 30, "江苏市昆山市");
            Person person3 = new Person("王五", 28, "太原市小店区");
            
            // 使用ObjectOutputStream的writeObject()方法将Person对象序列化并写入文件
            // writeObject()方法会自动将对象转换为字节序列并写入到输出流中
            // 每次调用writeObject()都会将一个对象完整地序列化并写入
            // 对象的类信息、属性名、属性值等都会被序列化保存
            oos.writeObject(person1);
            oos.writeObject(person2);
            oos.writeObject(person3);
            
            // 输出成功信息到控制台，告知用户对象已成功保存到文件中
            // System.out是标准输出流，默认指向控制台
            System.out.println("Person对象已成功保存到文件中");
        } catch (IOException e) {
            // 捕获并处理IO异常，可能的异常情况包括：
            // 1. 文件路径不存在或无法创建文件
            // 2. 没有写入文件的权限
            // 3. 磁盘空间不足
            // 4. 其他与文件I/O相关的异常
            // printStackTrace()方法会将异常的详细信息输出到标准错误流
            e.printStackTrace();
        } finally {
            // finally块中的代码无论是否发生异常都会执行
            // 这里用于确保流资源被正确关闭，避免资源泄露
            // 判断对象输出流是否为null，如果不为null则说明流已成功创建
            if (oos != null) {
                try {
                    // 调用close()方法关闭流，释放相关系统资源
                    // 关闭流后不能再进行读写操作
                    oos.close();
                } catch (IOException e) {
                    // 关闭流时也可能发生异常，需要进行捕获和处理
                    // 例如：流已经被关闭、系统资源访问异常等
                    e.printStackTrace();
                }
            }
        }
    }
    
    /**
     * load()方法：将save()方法中保存的Person对象读取出来，打印输出属性
     * 
     * 反序列化过程详解：
     * 1. 创建ObjectInputStream对象输入流，用于从文件中读取对象数据
     * 2. 使用readObject()方法从文件中逐个读取序列化的对象
     * 3. 将读取的对象强制转换为对应的类型
     * 4. 输出对象的属性信息到控制台
     * 5. 处理可能发生的IOException和ClassNotFoundException异常
     * 6. 在finally块中确保流资源被正确关闭
     * 
     * 反序列化的意义：
     * - 从持久化存储介质中恢复对象的状态
     * - 实现对象的持久化存储和读取
     * - 在分布式系统中恢复传输的对象
     * - 实现程序状态的保存和恢复功能
     */
    public static void load() {
        // 声明对象输入流引用，初始化为null，用于后续判断是否需要关闭资源
        // 使用ObjectInputStream可以从输入流中读取序列化的对象
        ObjectInputStream ois = null;
        try {
            // 创建文件输入流FileInputStream，指定输入文件名为"persons.dat"
            // FileInputStream是字节输入流，用于从文件中读取数据
            // 创建ObjectInputStream对象，将文件输入流作为参数传入
            // ObjectInputStream是对象输入流，可以将字节流反序列化为Java对象
            ois = new ObjectInputStream(new FileInputStream("persons.dat"));
            
            // 使用ObjectInputStream的readObject()方法从文件中读取序列化的对象
            // readObject()方法会自动将字节序列反序列化为Java对象
            // 每次调用readObject()都会读取一个完整的对象
            // 需要将读取的对象强制转换为具体的类型，这里是Person类型
            // 读取顺序必须与写入顺序一致，否则会出现类型转换异常
            Person person1 = (Person) ois.readObject();
            Person person2 = (Person) ois.readObject();
            Person person3 = (Person) ois.readObject();
            
            // 输出提示信息到控制台，告知用户即将显示从文件中读取的对象信息
            System.out.println("从文件中读取的Person对象：");
            // 调用Person对象的toString()方法，将对象信息输出到控制台
            // toString()方法在Person类中已被重写，会返回格式化的对象信息
            System.out.println(person1);
            System.out.println(person2);
            System.out.println(person3);
        } catch (IOException e) {
            // 捕获并处理IO异常，可能的异常情况包括：
            // 1. 文件不存在或无法访问
            // 2. 没有读取文件的权限
            // 3. 文件已损坏或格式不正确
            // 4. 其他与文件I/O相关的异常
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // 捕获并处理类未找到异常，当JVM无法找到序列化对象对应的类时抛出
            // 可能的异常情况包括：
            // 1. 序列化时的类在反序列化时已被删除或重命名
            // 2. 类路径配置不正确
            // 3. serialVersionUID不匹配
            e.printStackTrace();
        } finally {
            // finally块中的代码无论是否发生异常都会执行
            // 这里用于确保流资源被正确关闭，避免资源泄露
            // 判断对象输入流是否为null，如果不为null则说明流已成功创建
            if (ois != null) {
                try {
                    // 调用close()方法关闭流，释放相关系统资源
                    // 关闭流后不能再进行读写操作
                    ois.close();
                } catch (IOException e) {
                    // 关闭流时也可能发生异常，需要进行捕获和处理
                    // 例如：流已经被关闭、系统资源访问异常等
                    e.printStackTrace();
                }
            }
        }
    }
    
    /**
     * main()方法：程序的入口点，用于执行整个演示过程
     * 
     * 程序执行流程：
     * 1. 调用save()方法将Person对象序列化并保存到文件中
     * 2. 调用load()方法从文件中读取并反序列化Person对象
     * 3. 在控制台输出执行结果
     * 
     * main方法的特征：
     * - 必须是public static void类型
     * - 方法名必须是main
     * - 参数必须是String数组类型
     * - 是Java应用程序的入口点
     * - JVM会首先执行这个方法
     */
    public static void main(String[] args) {
        // 调用save静态方法，将创建的Person对象保存到文件中
        // 这是一个静态方法调用，不需要创建类的实例
        save();
        
        // 调用load静态方法，从文件中读取Person对象并打印输出
        // 这是一个静态方法调用，不需要创建类的实例
        load();
    }
}